﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LotteryVote.Entity;
using LotteryVote.Utility;
using System.Web;
using System.Web.Caching;
using LotteryVote.Data;
using LotteryVote.Logic.Exceptions;
using LotteryVote.Logic.Application;

namespace LotteryVote.Logic.Limit
{
    /// <summary>
    /// 上限限制器
    /// </summary>
    internal class UpperLimiter : ILimitCheck
    {
        #region properties
        private static object _lockHelper = new object();

        private DropWaterDataAccess _daDropWater;
        internal DropWaterDataAccess DaDropWater
        {
            get
            {
                if (_daDropWater == null)
                    _daDropWater = new DropWaterDataAccess();
                return _daDropWater;
            }
            set
            {
                _daDropWater = value;
            }
        }

        private DefaultUpperLimitDataAccess _daDefaultUpperLimit;
        internal DefaultUpperLimitDataAccess DaDefaultUpperLimit
        {
            get
            {
                if (_daDefaultUpperLimit == null)
                    _daDefaultUpperLimit = new DefaultUpperLimitDataAccess();
                return _daDefaultUpperLimit;
            }
            set
            {
                _daDefaultUpperLimit = value;
            }
        }

        private IDictionary<string, IList<DropWater>> _todayDropWater;
        protected IDictionary<string, IList<DropWater>> TodayDropWater
        {
            get
            {
                if (_todayDropWater == null)
                    _todayDropWater = new Dictionary<string, IList<DropWater>>();
                return _todayDropWater;
            }
        }
        #endregion

        public string ErrorMessage { get; private set; }
        /// <summary>
        /// 注入限制（若存在限制）.
        /// </summary>
        /// <param name="order">The order.</param>
        /// <returns></returns>
        public bool Inject(BetOrder order)
        {
            var upperLimit = GetUpperLimit(order);
            bool returnValue;
            if (returnValue = (!upperLimit.StopBet && Check(order, upperLimit)))
            {
                TransformOrder(order, upperLimit);
                upperLimit.TotalBetAmount += order.Amount;
            }
            return returnValue;
        }
        /// <summary>
        /// 回滚注单限制（用于撤销注单）.
        /// </summary>
        /// <param name="order">The order.</param>
        /// <returns></returns>
        public void RollOrderLimit(BetOrder order)
        {
            LogConsole.Debug(order.ToString());
            var upperLimit = GetUpperLimit(order);
            if (upperLimit.TotalBetAmount < order.Amount)
                throw new InvalidDataException("Error Data!Total bet amount less than order's amount!");
            var dropWaters = GetDropWater(order.Num, order.CompanyId, order.GamePlayWayId);

            upperLimit.TotalBetAmount -= order.Amount;        //注单总金额减少

            var previous = dropWaters.Where(it => it.Amount < upperLimit.NextLimit).        //找到上一个跌水规则
                GroupBy(it => it.Amount).
                OrderByDescending(it => it.Key).FirstOrDefault();

            DropWater dropwater = null;
            int previousDropCount = 0;
            if (previous != null)
            {
                foreach (var drop in previous)
                {
                    previousDropCount++;
                    if (dropwater != null && dropwater.DropType == DropType.Manual) continue;
                    dropwater = drop;
                }
                if (previousDropCount > 2)
                    throw new InvalidDataException(string.Format("号码:{0}的跌水记录大于2!CompanyId:{1} , GamePlayWayId:{2}",
                        upperLimit.Num, upperLimit.CompanyId, upperLimit.GamePlayWayId));
            }
            if (dropwater != null && dropwater.Amount > upperLimit.TotalBetAmount)      //如果跌水规则的金额 > 下注总金额
            {
                upperLimit.NextLimit = dropwater.Amount;
                upperLimit.DropValue -= dropwater.DropValue;
            }
        }
        /// <summary>
        /// 将更改的限制同步到数据库中
        /// </summary>
        public void UpdateCacheUpperLimitToDB(DataBase tandemDB = null)
        {
            LimitContext.Instance.UpdateCacheUpperLimitToDB(tandemDB);
        }
        /// <summary>
        /// 检查该注单是否可下.
        /// </summary>
        /// <param name="order">The order.</param>
        /// <param name="upperLimit">The upper limit.</param>
        /// <returns></returns>
        protected bool Check(Entity.BetOrder order, BetUpperLimit upperLimit)
        {
            decimal totalBetAmount = order.Amount + upperLimit.TotalBetAmount;
            if (totalBetAmount > upperLimit.UpperLlimit)        //最终投注金额大于投注上限金额
            {
                //TODO:错误信息呈现设计，存在多个插入失败
                ErrorMessage = "Can't Bet!";
                return false;
            }
            while (totalBetAmount > upperLimit.NextLimit)          //总投注金额小于投注上限，大于本次跌水金额限制（增加跌水）
            {
                UpdateUpperLimit(upperLimit);

                //本次跌水限制金额==总下注金额时则说明没有跌水限制了
                if (upperLimit.NextLimit == upperLimit.TotalBetAmount) break;
            }

            return true;
        }
        /// <summary>
        /// 获取注单相对应的上限限制记录.
        /// </summary>
        /// <param name="order">The order.</param>
        /// <returns></returns>
        protected BetUpperLimit GetUpperLimit(BetOrder order)
        {
            //获取指定公司，玩法，号码的上限限制
            var upperLimit = LimitContext.Instance.CacheUpperLimitList.Find(it =>
                it.CompanyId == order.CompanyId &&
                it.GamePlayWayId == order.GamePlayWayId &&
                it.Num == order.Num);
            if (upperLimit == null)     //获取默认上限限制
                upperLimit = GetDefaultUpperLimit(order.Num, order.CompanyId, order.GamePlayWayId);
            return upperLimit;
        }
        protected IList<DropWater> GetDropWater(string num, int companyId, int gameplaywayId)
        {
            string key = string.Format("{0}_{1}_{2}", num, companyId, gameplaywayId);
            if (!TodayDropWater.ContainsKey(key))
            {
                var dropWaters = DaDropWater.ListNumsDropWater(num, gameplaywayId, companyId, DateTime.Today);
                TodayDropWater.Add(key, dropWaters);
            }
            return TodayDropWater[key];
        }
        /// <summary>
        /// 获取默认上限限制
        /// </summary>
        /// <param name="num">The num.</param>
        /// <param name="companyId">The company id.</param>
        /// <param name="gameplaywayId">The gameplayway id.</param>
        /// <returns></returns>
        private BetUpperLimit GetDefaultUpperLimit(string num, int companyId, int gameplaywayId)
        {
            Func<BetUpperLimit> findUpperLimit = () => LimitContext.Instance.CacheUpperLimitList.Find(it =>
                it.CompanyId == companyId &&
                it.GamePlayWayId == gameplaywayId &&
                it.Num == num);
            var limit = findUpperLimit();
            if (limit == null)
            {
                lock (_lockHelper)
                {
                    limit = findUpperLimit();
                    if (limit == null)
                    {
                        var defaultUpplerLimit = DaDefaultUpperLimit.GetDefaultUpperLimit(gameplaywayId);
                        var limitAmount = defaultUpplerLimit != null ? defaultUpplerLimit.LimitAmount : LotterySystem.Current.DefaultUpperLimit;
                        var dropWaters = DaDropWater.ListNumsDropWater(num, gameplaywayId, companyId, DateTime.Today);
                        var minDropWaters = dropWaters.GroupBy(it => it.Amount).OrderBy(it => it.Key).FirstOrDefault();//最小金额跌水
                        int minDropCount = 0;
                        DropWater dropWater = null;
                        if (minDropWaters != null)
                        {
                            foreach (var drop in minDropWaters)
                            {
                                minDropCount++;
                                if (dropWater != null && dropWater.DropType == DropType.Manual) continue;
                                dropWater = drop;
                            }
                            if (minDropCount > 2)
                                throw new InvalidDataException(string.Format("号码:{0}的跌水记录大于2!CompanyId:{1} , GamePlayWayId:{2}", num, companyId, gameplaywayId));
                        }

                        limit = new BetUpperLimit
                        {
                            Num = num,
                            CompanyId = companyId,
                            GamePlayWayId = gameplaywayId,
                            DropValue = 0,
                            NextLimit = dropWater == null ? limitAmount : dropWater.Amount,
                            UpperLlimit = limitAmount,
                            TotalBetAmount = 0
                        };
                        LimitContext.Instance.BlUpperLimit.DaUpperLimit.Insert(limit);
                        LimitContext.Instance.CacheUpperLimitList.Add(limit);
                    }
                }
            }
            return limit;
        }
        private void UpdateUpperLimit(BetUpperLimit upperLimit)
        {
            var dropWaters = GetDropWater(upperLimit.Num, upperLimit.CompanyId, upperLimit.GamePlayWayId);
            var updropWaters = dropWaters.Where(it => it.Amount == upperLimit.NextLimit);   //获取下一个跌水限制
            DropWater updrop = null;
            //查找本次跌水
            foreach (var drop in updropWaters)
            {
                if (updrop != null && updrop.DropType == DropType.Manual) continue;
                updrop = drop;
            }
            if (updrop == null) LogConsole.Error(string.Format("号码:{0} 不存在金额为{1}的跌水限制!CompanyId:{2} , GamePlayWayId:{3}",
                upperLimit.Num, upperLimit.NextLimit, upperLimit.CompanyId, upperLimit.GamePlayWayId));
            else
                upperLimit.DropValue += updrop.DropValue;       //累加跌水值

            var minDrops = dropWaters.Where(it => it.Amount > upperLimit.NextLimit).GroupBy(it => it.Amount).OrderBy(it => it.Key).FirstOrDefault();
            int minDropCount = 0;
            DropWater dropWater = null;
            if (minDrops != null)
            {
                foreach (var drop in minDrops)
                {
                    minDropCount++;
                    if (dropWater != null && dropWater.DropType == DropType.Manual) continue;
                    dropWater = drop;
                }
                if (minDropCount > 2)
                    throw new InvalidDataException(string.Format("号码:{0}的跌水记录大于2!CompanyId:{1} , GamePlayWayId:{2}",
                        upperLimit.Num, upperLimit.CompanyId, upperLimit.GamePlayWayId));
            }

            if (minDropCount == 0 || dropWater == null)
                upperLimit.NextLimit = upperLimit.UpperLlimit;
            else
                upperLimit.NextLimit = dropWater.Amount;
        }
        private void TransformOrder(BetOrder order, BetUpperLimit upperLimit)
        {
            order.DropWater += upperLimit.DropValue;
            if (order.DropWater > 0)
            {
                order.Net += order.DropWater;
                double memberComm = (100 - order.Net).PercentageToDecimal(4);
                order.Commission = order.Turnover * (decimal)memberComm;
                order.NetAmount = order.Turnover - order.Commission;
                var companyComm = order.AncestorCommission.Find(it => it.RoleId == (int)SystemRole.Company);
                companyComm.Commission += order.DropWater;
                companyComm.CommAmount = (decimal)(100 - companyComm.Commission).PercentageToDecimal(4) * order.Turnover;
            }
        }
    }
}
